using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Windows.Forms;
using System.Configuration;
using Inikus.SlimTimer;
using System.Collections.ObjectModel;

namespace Inikus.WinTimer
{
    public partial class MainForm : Form
    {
        #region Declarations

        private TimeCounter _ActiveCounter;
        private DateTime _LastSaveTime;
        private string DataFile = "data.xml";
        private readonly string BackupDataFile = "backup.xml";
        private dsWinTimer.EntriesRow _CurrentEntry;
        private dsWinTimer.TasksRow _CurrentTask;
        private readonly List<TabPage> tabPages;
        SlimTimerApi _Api;
        readonly frmOptions _Options;
        private readonly string _Key;

        private const string SLIMTIMER_UPDATE_MESSAGE = "Updating SlimTimer...";

        #endregion

        #region Constructor

        public MainForm()
        {
            InitializeComponent();

            // we are keeping track of the tab pages since the hide method doesn't work,
            // so we will remove/add the pages as necessary
            tabPages = new List<TabPage>(4) {tabCustomer, tabTasks, tabEntries, tabEntryDetail};

            tcMain.TabPages.Remove(tabEntryDetail);
            tsSave.Visible = false;

            DataFile = Path.Combine(Application.StartupPath, DataFile);
            BackupDataFile = Path.Combine(Application.StartupPath, BackupDataFile);

            if (File.Exists(DataFile))
            {
                try
                {
                    dsWinTimer.ReadXml(DataFile);
                    // copy the successfully opened file to the backup in case of file corruption
                    File.Copy(DataFile, BackupDataFile, true);
                }
                catch
                {
                    // if there is an error opening the file, try opening the backup
                    // from the last successful startup
                    dsWinTimer.Clear();
                    dsWinTimer.ReadXml(BackupDataFile);
                    lblInternetStatus.Text = "Error opening data file, backup from last successful startup used.";
                }
            }
            else if (File.Exists(BackupDataFile))
            {
                dsWinTimer.ReadXml(BackupDataFile);
            }

            _Key = ConfigurationManager.AppSettings["ApiKey"];

            if (string.IsNullOrEmpty(_Key))
                _Key = "a919642c562e49fbea500851f77c37";

            _Options = new frmOptions();
            _Api = new SlimTimerApi(
                _Options.UserName,
                _Options.Password,
                _Key);
        }

        #endregion

        #region Tray Menu Events

        private void notifyIcon1_DoubleClick(object sender, EventArgs e)
        {
            openFormMenuItem.PerformClick();
        }

        private void startToolStripMenuItem_Click(object sender, EventArgs e)
        {
            btnStart.PerformClick();
        }

        private void stopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            btnStop.PerformClick();
            Show();
            WindowState = FormWindowState.Normal;
        }

        #endregion

        #region TimerEvent

        private void timeTicker_Tick(object sender, EventArgs e)
        {
            const int maxSaveMinutes = 5;
            uint idle = TimerLibrary.GetIdleTime();
            if (idle > (_Options.IdleTime * 60 * 1000)) // 5 minutes
            {
                if (_ActiveCounter.CurrentlyActive)
                {
                    // show the pause icon
                    notifyIcon1.Icon = Properties.Resources.Timer3;
                    // remove the time it took to become idle from the active time
                    _ActiveCounter.AddActiveTime(_Options.IdleTime * 60 * 1000 * -1);
                }
                _ActiveCounter.AddIdleTime(timeTicker.Interval);
            }
            else
            {
                if (!_ActiveCounter.CurrentlyActive)
                {
                    // show the running icon
                    notifyIcon1.Icon = Properties.Resources.Timer2;
                }
                _ActiveCounter.AddActiveTime(timeTicker.Interval);
            }

            TimeSpan TimeFromLastSave = DateTime.Now.Subtract(_LastSaveTime);

            // we save every 5 min
            if (TimeFromLastSave.TotalMinutes > maxSaveMinutes && _ActiveCounter.CurrentlyActive)
            {
                _LastSaveTime = DateTime.Now;
                // save data
                lock (_CurrentEntry)
                {
                    _CurrentEntry.Duration = _ActiveCounter.ActiveTime.TotalSeconds;
                    _CurrentEntry.DurationDisplay = _ActiveCounter.ActiveTime.ToString();
                    _CurrentEntry.EndTime = DateTime.Now;
                    _CurrentEntry.Synced = false;
                    if (_CurrentEntry.StartTime.Date != _CurrentEntry.EndTime.Date)
                    {
                        // if we've gone over midnight, then start a new time entry
                        _CurrentEntry.EndTime = _CurrentEntry.StartTime.Date.Add(new TimeSpan(23, 59, 59));
                        _CurrentEntry.Comments += " Auto ended at day end.";
                        _CurrentEntry.InProgress = false;
                        // reset the active time for the new counter
                        _ActiveCounter.ActiveTime = new TimeSpan();
                        _CurrentEntry = CreateEntriesRow();
                    }
                }
                SaveData(true);
            }

            SetStatus( string.Format("{0} [{1}]", _ActiveCounter.Name, _ActiveCounter.ActiveTime));
        }

        #endregion

        #region Button Events

        private void btnAdd_Click(object sender, EventArgs e)
        {
            SaveData(false);
            if (tcMain.SelectedTab == tabTasks)
            {
                dsWinTimer.TasksRow row = dsWinTimer.Tasks.NewTasksRow();
                row.TaskId = Guid.NewGuid();
                row.CreatedAt = DateTime.Now;
                row.UpdatedAt = DateTime.Now;
                if (dgvCustomers.SelectedRows.Count > 0)
                {
                    row.CustomerId = (Guid)dgvCustomers.SelectedRows[0].Cells["colCustomerId"].Value;
                }
                else
                    ckShowWOCustomer.Checked = true;

                row.Name = "-New Task-";
                dsWinTimer.Tasks.AddTasksRow(row);
                EnableStart(true);
                EnableStop(false);
            }
            else if (tcMain.SelectedTab == tabEntries)
            {
                if (lblTask.Tag == null)
                {
                    MessageBox.Show("Please select a task!", "Win Timer", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                dsWinTimer.EntriesRow row = CreateEntriesRow();
                tabEntryDetail.Tag = row;
                SetupEntryDetail();
            }
            else if (tcMain.SelectedTab == tabCustomer)
            {
                dsWinTimer.CustomersRow row = dsWinTimer.Customers.NewCustomersRow();
                row.CustomerId = Guid.NewGuid();
                row.Name = "-New Customer-";
                dsWinTimer.Customers.AddCustomersRow(row);
            }
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            if (txtEntryComment.Text.Trim() == String.Empty)
            {
                MessageBox.Show("Please enter a comment!", "Win Timer", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            if (dateEntryEnd.Value < dateEntryStart.Value)
            {
                MessageBox.Show("The end time cannot be before the start time!", "Win Timer", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            dsWinTimer.EntriesRow row = (dsWinTimer.EntriesRow)tabEntryDetail.Tag;
            //if (row.EntryId == Guid.Empty)
            //{
            //    row.EntryId = Guid.NewGuid();
            //    dsWinTimer.Entries.AddEntriesRow(row);
            //}

            // set the duration
            TimeSpan duration = timeDuration.Value.Subtract(timeDuration.Value.Date);
            row.Duration = Convert.ToInt32(duration.TotalSeconds);
            row.StartTime = dateEntryDate.Value.Date.Add(dateEntryStart.Value.TimeOfDay);
            row.EndTime = dateEntryDate.Value.Date.Add(dateEntryEnd.Value.TimeOfDay);
            // don't allow a 0 duration, it will cause a problem on the website
            if (row.Duration <= 0)
            {
                row.Duration = 1;
                row.EndTime.AddSeconds(1);
            }
            // be sure we don't show any time past the seconds
            row.DurationDisplay = duration.ToString().Substring(0, 8);
            row.InProgress = false;
            row.Synced = false;
            UpdateTaskHours(_CurrentTask);

            EndEdits(tabEntryDetail.Controls);
            EnableTabs(true);

            SaveData(true);
            //dgvTasks.SelectedRows[
            tcMain.SelectedTab = tabEntries;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (_CurrentEntry != null && _CurrentEntry.InProgress)
            {
                if (MessageBox.Show("Are you sure you would like to remove the current time entry?", "Win Timer",
                    MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    if (!_CurrentEntry.IsStEntryIdNull())
                        dsWinTimer.DeletedEntries.AddDeletedEntriesRow(_CurrentEntry.StEntryId);
                    dsWinTimer.Entries.RemoveEntriesRow(_CurrentEntry);
                    dsWinTimer.AcceptChanges();
                }
                else
                {
                    return;
                }
            }
            dsWinTimer.Entries.RejectChanges();
            EnableTabs(true);
            tcMain.SelectedTab = tabEntries;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            bool createNewEntry = true;

            if (tcMain.SelectedTab == tabEntries)
            {
                if (dgvEntries.SelectedRows.Count > 0 && 
                    Convert.ToDateTime(dgvEntries.SelectedRows[0].Cells["colStartTime"].Value).Date == DateTime.Today)
                {
                    createNewEntry = false;
                }
            }

            if (createNewEntry)
            {                    
                // create a new time entry
                _CurrentEntry = CreateEntriesRow();
                tcMain.SelectedTab = tabEntries;
                
                DataGridViewRow newRow = FindEntryRow(_CurrentEntry.EntryId.ToString());
                if (newRow != null)
                {
                    newRow.Selected = true;
                }
            }

            if (dgvEntries.SelectedRows.Count <= 0)
                return;

            Guid entryId = (Guid)dgvEntries.SelectedRows[0].Cells["colEntryId"].Value;
            _CurrentEntry = dsWinTimer.Entries.FindByEntryId(entryId);

            _CurrentEntry.InProgress = true;

            _ActiveCounter = new TimeCounter();
            // if this is a task that has already been startd, then add the current time
            _ActiveCounter.AddActiveTime(_CurrentEntry.Duration * 1000);
            timeTicker.Start();
            EnableStart(false);
            EnableStop(true);
            dgvEntries.Enabled = false;
            tsTasks.Enabled = false;
            _ActiveCounter.Name = lblTask.Text;
            _LastSaveTime = DateTime.Now;
            notifyIcon1.Icon = Properties.Resources.Timer2;
            SetStatus("Timer Starting...");
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            timeTicker.Stop();
            _CurrentEntry.EndTime = DateTime.Now;
            _CurrentEntry.Duration = _ActiveCounter.ActiveTime.TotalSeconds;
            _CurrentEntry.DurationDisplay = _ActiveCounter.ActiveTime.ToString();
            UpdateTaskHours(_CurrentTask);
            SaveData(false);

            dgvEntries.Enabled = true;
            EnableStart(true);
            EnableStop(false);
            tabEntryDetail.Tag = _CurrentEntry;
            SetupEntryDetail();
            txtEntryComment.Focus();
            notifyIcon1.Icon = Properties.Resources.Timer1;
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Are you sure you would like to delete the current row?", "WinTimer Delete",
                MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
                return;

            if (tcMain.SelectedTab == tabCustomer)
            {
                if (dgvCustomers.SelectedRows.Count > 0)
                {
                    dsWinTimer.CustomersRow deleted = dsWinTimer.Customers.FindByCustomerId(
                        (Guid)dgvCustomers.SelectedRows[0].Cells["colCustomerId"].Value);
                    // remove links to this customer from all tasks that link to it
                    DataRow[] linkedRows = dsWinTimer.Tasks.Select("CustomerId = '" + deleted.CustomerId.ToString() + "'");
                    foreach (dsWinTimer.TasksRow row in linkedRows)
                    {
                        row["CustomerId"] = DBNull.Value;
                    }
                    dsWinTimer.Customers.RemoveCustomersRow(deleted);
                }
            }
            else if (tcMain.SelectedTab == tabEntries)
            {
                if (dgvEntries.SelectedRows.Count > 0)
                {
                    ClearEntryBindings(tabEntryDetail.Controls);
                    Guid entryId = (Guid)dgvEntries.SelectedRows[0].Cells["colEntryId"].Value;
                    dsWinTimer.EntriesRow deleted = dsWinTimer.Entries.FindByEntryId(entryId);
                    dsWinTimer.TasksRow parentTask = dsWinTimer.Tasks.FindByTaskId(deleted.TaskId);
                    // if it's null we haven't synced yet, so no reason to add it to the deleted table
                    if (!deleted.IsStEntryIdNull()) 
                        dsWinTimer.DeletedEntries.AddDeletedEntriesRow(deleted.StEntryId);
                    dsWinTimer.Entries.RemoveEntriesRow(deleted);
                    // be sure to update the hours of the task
                    UpdateTaskHours(parentTask);
                }
            }
            else if (tcMain.SelectedTab == tabTasks)
            {
                if (dgvTasks.SelectedRows.Count > 0)
                {
                    Guid taskId = (Guid)dgvTasks.SelectedRows[0].Cells["colTaskId"].Value;
                    dsWinTimer.TasksRow deleted = dsWinTimer.Tasks.FindByTaskId(taskId);
                    // remove all linked entries
                    DataRow[] linkedRows = dsWinTimer.Entries.Select("TaskId = '" + deleted.TaskId.ToString() + "'");
                    foreach (dsWinTimer.EntriesRow row in linkedRows)
                    {
                        if (!row.IsStEntryIdNull())
                            dsWinTimer.DeletedEntries.AddDeletedEntriesRow(row.StEntryId);
                        dsWinTimer.Entries.RemoveEntriesRow(row);
                    }

                    // if it's null we haven't synced yet, so no reason to add it to the deleted table
                    if (!deleted.IsStTaskIdNull())
                        dsWinTimer.DeletedTasks.AddDeletedTasksRow(deleted.StTaskId);
                    dsWinTimer.Tasks.RemoveTasksRow(deleted);
                }
            }
            SaveData(true);
        }

        private void btnEdit_Click(object sender, EventArgs e)
        {
            SaveData(false);
            if (tcMain.SelectedTab == tabEntries)
            {
                if (dgvEntries.SelectedRows.Count > 0)
                {
                    Guid entryId = (Guid)dgvEntries.SelectedRows[0].Cells["colEntryId"].Value;
                    tabEntryDetail.Tag = dsWinTimer.Entries.FindByEntryId(entryId);
                    SetupEntryDetail();
                    tcMain.SelectedTab = tabEntryDetail;
                }
            }
            else if (tcMain.SelectedTab == tabTasks)
            {
                if (dgvTasks.SelectedRows.Count > 0)
                {
                    dgvTasks.BeginEdit(true);
                }
            }
            else if (tcMain.SelectedTab == tabCustomer)
            {
                if (dgvCustomers.SelectedRows.Count > 0)
                {
                    dgvCustomers.BeginEdit(true);
                }
            }
        }

        #endregion

        #region Misc Methods

        private void tcMain_Selecting(object sender, TabControlCancelEventArgs e)
        {
            if (e.TabPage == null)
                return;

            if (e.TabPage == tabEntryDetail)
            {
                if (!SetupEntryDetail())
                    e.Cancel = true;
            }
            else
            {
                EnableTabs(true);
                tsTasks.Enabled = true;
                tsTimer.Enabled = true;
                tsSave.Visible = false;
            }

            if (e.TabPage == tabEntries)
            {
                if (dgvTasks.Rows.Count > 0 && dgvTasks.SelectedRows.Count == 0)
                    dgvTasks.Rows[0].Selected = true;

                if (dgvEntries.Rows.Count > 0)
                {
                    if (dgvEntries.SelectedRows.Count <= 0)
                        dgvEntries.Rows[0].Selected = true;
                }

                if (_ActiveCounter == null && dgvTasks.SelectedRows.Count > 0)
                    EnableStart(true);
                else if (_ActiveCounter == null)
                    EnableStart(false);
                
                dsWinTimerBindingSource.RemoveFilter();
                if (dgvTasks.SelectedRows.Count > 0)
                {
                    // filter the entries to only show the ones for the current task
                    entriesBindingSource.Filter = "TaskId = '" + dgvTasks.SelectedRows[0].Cells["colTaskId"].Value.ToString() + "'";
                    entriesBindingSource.Sort = "StartTime DESC";
                }
            }
            else if (e.TabPage == tabTasks)
            {
                if (dgvTasks.Rows.Count > 0)
                {
                    if (dgvTasks.SelectedRows.Count <= 0)
                        dgvTasks.Rows[0].Selected = true;

                    _CurrentTask = dsWinTimer.Tasks.FindByTaskId(
                        (Guid)dgvTasks.SelectedRows[0].Cells["colTaskId"].Value);
                    lblTask.Text = _CurrentTask.Name;
                    lblTask.Tag = _CurrentTask.TaskId;

                    if (_ActiveCounter == null)
                        EnableStart(true);
                }
            }
            else
            {
                if (_ActiveCounter == null)
                    EnableStart(false);
            }
        }

        private void dgvEntries_DoubleClick(object sender, EventArgs e)
        {
            btnEdit.PerformClick();
        }

        private void dgvTasks_DoubleClick(object sender, EventArgs e)
        {
            if (dgvTasks.SelectedRows.Count > 0)
            {
                entriesBindingSource.Filter = "TaskId = '" + dgvTasks.SelectedRows[0].Cells["colTaskId"].Value.ToString() + "'";
                tcMain.SelectedTab = tabEntries;
            }
        }

        private void dgvTasks_SelectionChanged(object sender, EventArgs e)
        {
            if (dgvTasks.Rows.Count > 0 && dgvTasks.SelectedRows.Count == 0)
                dgvTasks.Rows[0].Selected = true;

            if (dgvTasks.SelectedRows.Count > 0)
            {
                _CurrentTask = dsWinTimer.Tasks.FindByTaskId(
                    (Guid)dgvTasks.SelectedRows[0].Cells["colTaskId"].Value);
                lblTask.Text = _CurrentTask.Name;
                lblTask.Tag = _CurrentTask.TaskId;
            }
            else
            {
                lblTask.Text = "-";
                lblTask.Tag = null;
                _CurrentTask = null;
            }
        }

        private void dgvTasks_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            // reset the synced property when the user makes a change
            // and ensure the correct task name is showing in the status bar
            if (e.RowIndex >= 0)
            {
                dgvTasks.Rows[e.RowIndex].Cells["colTaskSynced"].Value = false;
                lblTask.Text = _CurrentTask.Name;
            }
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            SaveData(false);
        }

        private void dgvCustomers_SelectionChanged(object sender, EventArgs e)
        {
            // unchecking the checkbox will automatically set the filter, so we don't need to do it twice
            if (ckShowWOCustomer.Checked)
                ckShowWOCustomer.Checked = false;
            else
                SetTaskFilter();
        }

        private void ckShowWOCustomer_CheckedChanged(object sender, EventArgs e)
        {
            SetTaskFilter();
        }

        private void ckShowCompleted_CheckedChanged(object sender, EventArgs e)
        {
            SetTaskFilter();
            if (dgvTasks != null && dgvTasks.Columns != null)
                dgvTasks.Columns["colCompleted"].HeaderText = ckShowCompleted.Checked ? "UnMark" : "Mark";
        }

        private void dgvTasks_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            // this is the completed cell
            if (dgvTasks != null && dgvTasks.Columns != null)
                if (e.ColumnIndex == dgvTasks.Columns["colCompleted"].Index)
                {
                    if (dgvTasks["colCompletedOn", e.RowIndex].Value == DBNull.Value ||
                        dgvTasks["colCompletedOn", e.RowIndex].Value == null)
                    {
                        dgvTasks["colCompletedOn", e.RowIndex].Value = DateTime.Now;
                    }
                    else
                    {
                        // they must be setting this to uncompleted
                        dgvTasks["colCompletedOn", e.RowIndex].Value = DBNull.Value;
                    }
                    SetTaskFilter();
                }
        }

        private void MainForm_Load(object sender, EventArgs e)
        {

            //if (System.Diagnostics.Process.GetProcessesByName(System.Diagnostics.Process.GetCurrentProcess().ProcessName).Length > 0)
            //{
            //    //Send opening form's TEXT property as a parameter to the function "ActivatePrevInstance"
            //    //This works well with an MDI form or a non-MDI form
            //    //It is advised that you give a Unique name to your Form so that it does not conflict with other applications
            //    ActivatePrevInstance(this.Text);
            //}
        }

        private void timeDuration_ValueChanged(object sender, EventArgs e)
        {
            TimeSpan duration = timeDuration.Value.Subtract(timeDuration.Value.Date);
            if (dateEntryStart.Value.Add(duration) > dateEntryEnd.Value)
            {
                try
                {
                    dateEntryEnd.ValueChanged -= dateEntryStart_ValueChanged;
                    dateEntryEnd.Value = dateEntryStart.Value.Add(duration);
                }
                finally
                {
                    dateEntryEnd.ValueChanged += dateEntryStart_ValueChanged;
                }
            }
        }

        private void dateEntryStart_ValueChanged(object sender, EventArgs e)
        {
            //if (sender == dateEntryStart)
            //{
            //    TimeSpan EndTime = dateEntryEnd.Value.Subtract(dateEntryEnd.Value.Date);
            //    // the start date can't be before the end date
            //    if (dateEntryStart.Value > dateEntryEnd.Value)
            //    {
            //        dateEntryEnd.Value = dateEntryStart.Value;
            //    }
            //}
            //else if (sender == dateEntryEnd)
            //{
            //    TimeSpan StartTime = dateEntryStart.Value.Subtract(dateEntryStart.Value.Date);
            //    // the start date can't be before the end date
            //    if (dateEntryStart.Value > dateEntryEnd.Value)
            //    {
            //        dateEntryStart.Value = dateEntryEnd.Value;
            //    }
            //}

            if (dateEntryEnd.Value > dateEntryStart.Value)
            {
                TimeSpan duration = dateEntryEnd.Value.Subtract(dateEntryStart.Value);
                timeDuration.Value = dateEntryEnd.Value.Date.Add(duration);
            }
        }

        #endregion

        #region Grid Filtering

        private void SetTaskFilter()
        {
            if (dgvCustomers.SelectedRows.Count > 0 && !ckShowWOCustomer.Checked)
            {
                tasksBindingSource.Filter = "CustomerId = '" +
                        dgvCustomers.SelectedRows[0].Cells["colCustomerId"].Value.ToString() + "' AND " +
                        GetCompletedFilter();
            }
            else if (ckShowWOCustomer.Checked)
            {
                if (dgvCustomers.SelectedRows.Count > 0)
                {
                    try
                    {
                        dgvCustomers.SelectionChanged -= dgvCustomers_SelectionChanged;
                        dgvCustomers.SelectedRows[0].Selected = false;
                    }
                    finally
                    {
                        dgvCustomers.SelectionChanged += dgvCustomers_SelectionChanged;
                    }
                }

                tasksBindingSource.Filter = "CustomerId IS NULL AND " + GetCompletedFilter();
            }
            else
            {
                tasksBindingSource.Filter = GetCompletedFilter();
            }
        }

        private string GetCompletedFilter()
        {
            if (ckShowCompleted.Checked)
                return "CompletedOn IS NOT NULL";
            return "CompletedOn IS NULL";
        }

        #endregion

        #region Background Worker

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            UpdateSlimTimer();
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            lblInternetStatus.Text = e.UserState.ToString().Trim().Length > 500 ? 
                e.UserState.ToString().Trim().Substring(0, 500) : e.UserState.ToString().Trim();
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // don't erase an error message if one exists
            if (lblInternetStatus.Text == SLIMTIMER_UPDATE_MESSAGE)
                lblInternetStatus.Text = "SlimTimer Updates Complete";
        }

        #endregion

        #region Menu Methods

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
            SaveData(false);
            Application.Exit();
        }
        
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveData(true);
            OpenFileDialog openDialog = new OpenFileDialog
                                            {
                                                Filter = "data files (*.xml)|*.xml|All files (*.*)|*.*",
                                                ValidateNames = true,
                                                Multiselect = false,
                                                CheckPathExists = true,
                                                CheckFileExists = true
                                            };
            if (openDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    dsWinTimer.Clear();
                    DataFile = openDialog.FileName;
                    dsWinTimer.ReadXml(openDialog.FileName);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            frmAbout f = new frmAbout();
            f.ShowDialog(this);
        }

        private void backupToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveDialog = new SaveFileDialog
                                            {
                                                Filter = "backup files (*.wtb)|*.wtb|All files (*.*)|*.*",
                                                DefaultExt = ".wtb"
                                            };
            if (saveDialog.ShowDialog() == DialogResult.OK)
            {
                SaveData(false);
                // compress xml file after saving
                using (Stream s = new ICSharpCode.SharpZipLib.GZip.GZipOutputStream(File.Create(saveDialog.FileName)))
                {
                    using (FileStream fs = File.OpenRead(DataFile))
                    {
                        byte[] writeData = new byte[4096];
                        ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fs, s, writeData);
                    }
                }
            }
        }

        private void restoreToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog openDialog = new OpenFileDialog
                                            {
                                                Filter = "backup files (*.wtb)|*.wtb|All files (*.*)|*.*",
                                                DefaultExt = ".wtb",
                                                CheckFileExists = true
                                            };
            if (openDialog.ShowDialog() == DialogResult.OK)
            {
                using (Stream inStream = new ICSharpCode.SharpZipLib.GZip.GZipInputStream(File.OpenRead(openDialog.FileName)))
                {
                    using (FileStream outStream = File.Create(DataFile))
                    {
                        byte[] buffer = new byte[4096];
                        ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(inStream, outStream, buffer);
                    }
                }
                dsWinTimer.Clear();
                dsWinTimer.ReadXml(DataFile);
            }
        }

        private void openToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            Show();
            WindowState = FormWindowState.Normal;
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                Hide();
            }
        }

        private void updateSlimTimerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveData(true);
        }

        private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _Options.ShowDialog(this);
            // be sure we are using the latest
            _Api = new SlimTimerApi(_Options.UserName, _Options.Password, _Key);
        }

        private void reportsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            frmReports f = new frmReports(dsWinTimer);
            f.ShowDialog(this);
        }

        #endregion

        #region Helper Methods

        //private DataRow GetCurrentRow(DataGridView grid)
        //{
        //    BindingManagerBase bm = grid.BindingContext[grid.DataSource, grid.DataMember];

        //    return ((DataRowView)bm.Current).Row;

        private dsWinTimer.EntriesRow CreateEntriesRow()
        {
            // create a new time entry
            dsWinTimer.EntriesRow newRow = dsWinTimer.Entries.NewEntriesRow();
            newRow.EntryId = Guid.NewGuid();
            newRow.StartTime = DateTime.Now;
            newRow.EndTime = DateTime.Now; // give it a default end time, this will change
            newRow.Duration = 0;
            newRow.TaskId = (Guid)lblTask.Tag;
            newRow.CreatedAt = DateTime.Now;
            newRow.InProgress = true;
            dsWinTimer.Entries.AddEntriesRow(newRow);
            return newRow;
        }

        private void SetStatus(string status)
        {
            lblStatus1.Text = status;
            notifyIcon1.Text = status;
        }

        private void EnableStart(bool enable)
        {
            btnStart.Enabled = enable;
            startToolStripMenuItem.Enabled = enable;
        }

        private void EnableStop(bool enable)
        {
            btnStop.Enabled = enable;
            stopToolStripMenuItem.Enabled = enable;
        }

        private void SaveData(bool updateRemote)
        {
            try
            {
                BeginLoadData();
                lblInternetStatus.Text = string.Empty;
                // we also need to write to the SlimTimer API
                if (!backgroundWorker1.IsBusy && updateRemote && _Options.SlimTimerEnabled)
                {
                    lblInternetStatus.Text = SLIMTIMER_UPDATE_MESSAGE;
                    backgroundWorker1.RunWorkerAsync();
                }

                dsWinTimer.AcceptChanges();
                dsWinTimer.WriteXml(DataFile, XmlWriteMode.IgnoreSchema);
            }
            finally
            {
                EndLoadData();
            }
        }

        private Task GetTaskFromRow(dsWinTimer.TasksRow row)
        {
            Task task = new Task(row.Name);
            task.Id = row.IsStTaskIdNull() ? null : row.StTaskId;
            if (!row.IsCustomerIdNull())
            {
                dsWinTimer.CustomersRow cust = dsWinTimer.Customers.FindByCustomerId(row.CustomerId);
                if (cust != null)
                    task.Tags.Add("{" + cust.Name + "}");
            }
            if (!row.IsTagsNull())
            {
                if (row.Tags.Trim().Length > 0)
                    task.Tags.AddRange(row.Tags.Split(','));
            }
            //task.CoWorkerEmails = 
            if (!row.IsCompletedOnNull())
                task.CompletedOn = row.CompletedOn;

            task.ReporterEmails.AddRange(_Options.Reporters);
            task.CoworkerEmails.AddRange(_Options.CoWorkers);

            return task;
        }

        private static void SetTaskRowValues(dsWinTimer.TasksRow row, Task task)
        {
            row.StTaskId = task.Id;
            row.Tags = string.Empty;
            foreach (string tag in task.Tags)
                row.Tags += tag + ",";
            row.Tags.TrimEnd(',');
            row.UpdatedAt = task.UpdatedTime;
            row.Name = task.Name;
            row.Hours = task.Hours;
            // using the column name so we don't have to do an explicit conversion
            row["CompletedOn"] = task.CompletedOn ?? DBNull.Value;
            row.Synced = true;
        }

        private static void SetEntryRowValues(dsWinTimer.EntriesRow row, TimeEntry entry)
        {
            row.StEntryId = entry.Id;
            row.StartTime = entry.StartTime;
            row.EndTime = entry.EndTime;
            row.InProgress = entry.InProgress;

            row.Tags = "";
            foreach (string tag in entry.Tags)
                row.Tags += tag + ",";
            row.Tags.TrimEnd(',');

            row.UpdatedAt = entry.UpdatedTime;
            row.CreatedAt = entry.CreatedTime;
            row.Duration = entry.Duration;
            row.DurationDisplay = new TimeSpan(0, 0, entry.Duration).ToString();
        }

        private void GetSlimTimerRecords()
        {
            try
            {
                Collection<Task> tasks = _Api.ListTasks(SlimTimerApi.ShowCompletedTask.No, SlimTimerApi.TaskFilters.Owner);
                lock (dsWinTimer.Tasks)
                {
                    foreach (Task task in tasks)
                    {
                        // we are only doing updates and inserts, not deletes
                        DataRow[] matchingTasks = dsWinTimer.Tasks.Select("StTaskId = " + task.Id);
                        if (matchingTasks != null && matchingTasks.Length > 0)
                        {
                            dsWinTimer.TasksRow currentTaskRow = (dsWinTimer.TasksRow)matchingTasks[0];
                            // don't update from the server if we have updates to send
                            if (currentTaskRow.Synced)
                            {
                                SetTaskRowValues(currentTaskRow, task);
                            }
                        }
                        else // this is a new task, so insert it
                        {
                            dsWinTimer.TasksRow newRow = dsWinTimer.Tasks.NewTasksRow();
                            newRow.TaskId = Guid.NewGuid();
                            SetTaskRowValues(newRow, task);
                            dsWinTimer.Tasks.AddTasksRow(newRow);
                        }
                    }
                }

                // now get any time entries
                Collection<TimeEntry> entries = _Api.ListTimeEntries(DateTime.Today.AddDays(-60), DateTime.Now);
                lock (dsWinTimer.Entries)
                {
                    foreach (TimeEntry entry in entries)
                    {
                        // we are only doing updates and inserts, not deletes
                        DataRow[] matchingEntries = dsWinTimer.Entries.Select("StEntryId = " + entry.Id);
                        if (matchingEntries != null && matchingEntries.Length > 0)
                        {
                            dsWinTimer.EntriesRow currentEntryRow = (dsWinTimer.EntriesRow)matchingEntries[0];
                            // don't update from the server if we have updates to send
                            if (currentEntryRow.Synced)
                            {
                                SetEntryRowValues(currentEntryRow, entry);
                            }
                        }
                        else // this is a new task, so insert it
                        {
                            dsWinTimer.EntriesRow newRow = dsWinTimer.Entries.NewEntriesRow();
                            newRow.TaskId = Guid.NewGuid();
                            SetEntryRowValues(newRow, entry);
                            dsWinTimer.Entries.AddEntriesRow(newRow);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                backgroundWorker1.ReportProgress(0, ex.Message);
            }
        }

        private void UpdateSlimTimer()
        {
            try
            {
                DataRow[] nonSyncedTasks = dsWinTimer.Tasks.Select("Synced = 0");
                foreach (dsWinTimer.TasksRow row in nonSyncedTasks)
                {
                    Task task = GetTaskFromRow(row);
                    Task updatedTask = _Api.UpdateTask(task);
                    // there was a problem updating, so quit trying
                    if (updatedTask == null)
                    {
                        backgroundWorker1.ReportProgress(0, _Api.ErrorMessage);
                        continue;
                    }

                    // be sure we have the SlimTimer task id
                    lock (row)
                    {
                        row.StTaskId = updatedTask.Id;
                        row.Synced = true;
                    }
                    if (backgroundWorker1.CancellationPending)
                        return;
                }

                DataRow[] nonSyncedEntries = dsWinTimer.Entries.Select("Synced = 0");
                foreach (dsWinTimer.EntriesRow row in nonSyncedEntries)
                {
                    dsWinTimer.TasksRow taskIdRow = dsWinTimer.Tasks.FindByTaskId(row.TaskId);
                    TimeEntry entry = new TimeEntry(GetTaskFromRow(taskIdRow));
                    entry.Id = row.IsStEntryIdNull() ? null : row.StEntryId;
                    entry.StartTime = row.StartTime;
                    entry.EndTime = row.EndTime;
                    entry.Duration = (int)Math.Round(row.Duration);
                    if (row.Tags.Trim().Length > 0)
                        entry.Tags.AddRange(row.Tags.Split(','));
                    entry.Comments = row.Comments;
                    entry.InProgress = row.InProgress;

                    TimeEntry updatedEntry = _Api.UpdateTimeEntry(entry);
                    if (updatedEntry == null)
                    {
                        backgroundWorker1.ReportProgress(0, _Api.ErrorMessage);
                        continue;
                    }

                    lock (row)
                    {
                        row.StEntryId = updatedEntry.Id;
                        row.Synced = true;
                    }
                    if (backgroundWorker1.CancellationPending)
                        return;
                }

                bool itemDeleted = false;

                // Delete any rows in the deleted items tables
                for (int i = dsWinTimer.DeletedEntries.Count - 1; i >= 0; i--)
                {
                    dsWinTimer.DeletedEntriesRow row = (dsWinTimer.DeletedEntriesRow)dsWinTimer.DeletedEntries.Rows[i];
                    try
                    {
                        itemDeleted = _Api.DeleteTimeEntry(row.StEntryId);
                    }
                    catch (System.Net.WebException webEx)
                    {
                        if (webEx.Message.IndexOf("404") > 0)
                        {
                            // if it's a 404 error, then the record doesn't exist on the server, so ignore it
                            itemDeleted = true;
                        }
                        else
                        {
                            throw;
                        }
                    }
                    if (itemDeleted)
                    {
                        dsWinTimer.DeletedEntries.RemoveDeletedEntriesRow(row);
                    }
                }
                for (int i = dsWinTimer.DeletedTasks.Count - 1; i >= 0; i--)
                {
                    dsWinTimer.DeletedTasksRow row = (dsWinTimer.DeletedTasksRow)dsWinTimer.DeletedTasks.Rows[i];
                    try
                    {
                        itemDeleted = _Api.DeleteTask(row.StTaskId);
                    }
                    catch (System.Net.WebException webEx)
                    {
                        if (webEx.Message.IndexOf("404") > 0)
                        {
                            // if it's a 404 error, then the record doesn't exist on the server, so ignore it
                            itemDeleted = true;
                        }
                    }
                    if (itemDeleted)
                    {
                        dsWinTimer.DeletedTasks.RemoveDeletedTasksRow(row);
                    }
                }
            }
            catch (Exception ex)
            {
                if (_Api.ErrorMessage.Length > 0)
                    backgroundWorker1.ReportProgress(0, _Api.ErrorMessage);
                else
                    backgroundWorker1.ReportProgress(0, ex.Message);
                Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(ex, "General");
            }
        }

        private static void ClearEntryBindings(Control.ControlCollection controls)
        {
            foreach (Control c in controls)
            {
                c.DataBindings.Clear();
                if (c.HasChildren)
                    ClearEntryBindings(c.Controls);
            }
        }

        private void EnableTabs(bool enable)
        {
            try
            {
                BeginLoadData();
                if (!enable)
                {
                    if (!tcMain.TabPages.Contains(tabEntryDetail))
                        tcMain.TabPages.Add(tabEntryDetail);

                    tcMain.TabPages.Remove(tabCustomer);
                    tcMain.TabPages.Remove(tabTasks);
                    tcMain.TabPages.Remove(tabEntries);
                }
                else
                {
                    foreach (TabPage p in tabPages)
                    {
                        if (!tcMain.TabPages.Contains(p) && p != tabEntryDetail)
                            tcMain.TabPages.Add(p);
                    }

                    tcMain.TabPages.Remove(tabEntryDetail);
                }
            }
            finally
            {
                EndLoadData();
            }
        }

        private static void EndEdits(Control.ControlCollection cc)
        {
            foreach (Control c in cc)
            {
                EndEdits(c.Controls);
                foreach (Binding b in c.DataBindings)
                {
                    if (b.BindingManagerBase != null)
                        b.BindingManagerBase.EndCurrentEdit();
                }
            }
        }

        private void UpdateTaskHours(dsWinTimer.TasksRow task)
        {
            float totalHours = 0;
            DataRow[] entries = dsWinTimer.Entries.Select("TaskId = '" + task.TaskId + "'");
            foreach (dsWinTimer.EntriesRow row in entries)
            {
                totalHours += (float)(row.Duration / 60) / 60;
            }

            task.Hours = (float)Math.Round(totalHours, 1);
        }

        private bool SetupEntryDetail()
        {
            if (tabEntryDetail.Tag == null)
            {
                return false;
            }

            ClearEntryBindings(tabEntryDetail.Controls);
            EnableTabs(false);
            tsTasks.Enabled = false;
            tsTimer.Enabled = false;
            tsSave.Visible = true;

            dsWinTimer.EntriesRow row = (dsWinTimer.EntriesRow)tabEntryDetail.Tag;

            cmbEntryTask.DataBindings.Add("SelectedValue", row, "TaskId");
            txtEntryTags.DataBindings.Add("Text", row, "Tags");
            txtEntryComment.DataBindings.Add("Text", row, "Comments");
            dateEntryDate.Value = row.StartTime.Date;
            dateEntryStart.Value = row.StartTime;
            dateEntryEnd.Value = row.EndTime;
            //dateEntryStart.DataBindings.Add("Value", row, "StartTime");
            //dateEntryEnd.DataBindings.Add("Value", row, "EndTime");
            nudDuration.DataBindings.Add("Value", row, "Duration");
            timeDuration.Value = dateEntryEnd.Value.Date.AddSeconds(row.Duration);
            return true;
        }

        private void BeginLoadData()
        {
            foreach (DataTable table in dsWinTimer.Tables)
            {
                table.BeginLoadData();
            }

            tcMain.Selecting -= tcMain_Selecting;
            dgvTasks.SelectionChanged -= dgvTasks_SelectionChanged;
            dgvCustomers.SelectionChanged -= dgvCustomers_SelectionChanged;
        }

        private void EndLoadData()
        {
            foreach (DataTable table in dsWinTimer.Tables)
            {
                table.EndLoadData();
            }

            tcMain.Selecting += tcMain_Selecting;
            dgvTasks.SelectionChanged += dgvTasks_SelectionChanged;
            dgvCustomers.SelectionChanged += dgvCustomers_SelectionChanged;
        }

        private DataGridViewRow FindEntryRow(string entryId)
        {
            foreach (DataGridViewRow row in dgvEntries.Rows)
            {
                if (row.Cells["colEntryId"].Value.ToString() == entryId)
                    return row;
            }

            return null;
        }

        //Declarations of Windows API functions
        [System.Runtime.InteropServices.DllImport("User32.dll")]
        private static extern long OpenIcon(long hwnd);
        [System.Runtime.InteropServices.DllImport("User32.dll")]
        private static extern long SetForegroundWindow(long hwnd);

        /// <summary>
        /// This is here to prevent two instances running at the same time
        /// </summary>
        /// <param name="text"></param>
        private void ActivatePrevInstance(string text)
        {

            long PrevHndl = 0;

            // gets all processes on the machine
            System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcesses();

            foreach (System.Diagnostics.Process process in processes)
            {
                if (process.MainWindowTitle == text)
                {
                    PrevHndl = process.MainWindowHandle.ToInt64();
                }
            }

            if (PrevHndl == 0)
                return;

            OpenIcon(PrevHndl);
            SetForegroundWindow(PrevHndl);
        }

        #endregion

    }

}